using Cysharp.Threading.Tasks;
using M13.Base;
using M13.Game.Entity.Player;
using M13.GameSystem;
using M13.Math;
using M13.Utility;
using M13.Utility.Debugger;
using QFramework;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Serialization;

namespace M13.Game.World
{
    [Serializable]
    public struct SaveConfig
    {
        public string name;
        public int seed;
        public int saveId;
        public string time;
    }
    public class World : MonoBehaviour, IController, ICanSendEvent
    {
        public Queue<Vector2Int> ChunkLoadTask { get; private set; } = new Queue<Vector2Int>();
        public Queue<Vector2Int> ChunkUnloadTask { get; private set; } = new Queue<Vector2Int>();

        [FormerlySerializedAs("_chunkAroundPlayerIn")] public List<Vector2Int> chunkAroundPlayerIn = new List<Vector2Int>();

        public Dictionary<Vector2Int, Region> RegionMap { get; private set; } = new Dictionary<Vector2Int, Region>();

        public Dictionary<Vector2Int, GameObject> ChunkObjMap { get; private set; } = new Dictionary<Vector2Int, GameObject>();

        [Header("�浵������Ϣ")]
        [SerializeField] private SaveConfig saveConfig;

        [Space(10)]
        public bool worldInitFinished = false;

        public WorldDynamicLoadStatus worldDynamicLoadStatus = WorldDynamicLoadStatus.Empty;

        private ISaveSystem _saveSystem;

        [FormerlySerializedAs("_player")]
        [Space(10)]
        [SerializeField] private Player player;
        [SerializeField] private Vector2 playerV2Pos;

        [SerializeField] private Vector2Int chunkLocalCoordPlayerIn;
        [SerializeField] private Vector2Int lastChunkLocalCoordPlayerIn;

        private Vector2Int[] _adjacentChunks = new Vector2Int[4] { new Vector2Int(-1, 0), new Vector2Int(0, 1), new Vector2Int(1, 0), new Vector2Int(0, -1) };

        private int _chunkLoadRange = 15;
        
        private byte[] _cvm;
        private byte[] _chm;
        private byte[] _ctim;

        private void Start()
        {
            _saveSystem = this.GetSystem<ISaveSystem>();
            saveConfig = this.GetModel<IGameModal>().GameSaveConfig.Value;
            playerV2Pos = new Vector2(player.transform.position.x, player.transform.position.z);
            chunkLocalCoordPlayerIn = new Vector2Int((int)playerV2Pos.x >> 4, (int)playerV2Pos.y >> 4);
            UpdateChunkAroundPlayer(true);
        }

        private void Update()
        {
            UpdateLoadStatus();

            playerV2Pos = new Vector2(player.transform.position.x, player.transform.position.z);
            chunkLocalCoordPlayerIn = new Vector2Int((int)playerV2Pos.x >> 4, (int)playerV2Pos.y >> 4);

            UpdateChunkAroundPlayer(false);

            PerformLoadChunkObjTask();
            PerformUnloadChunkObjTask();

        }

        private void FixedUpdate()
        {
            
        }

        #region 区块任务
       
        private void SpecificChunkData(Vector2Int chunkLocalCoord)
        {
            Vector2Int regionCoord = M13Mathf.ChunkToRegionCoord(chunkLocalCoord);
            if (!RegionMap.ContainsKey(regionCoord))
            {
                Region region = RegionDataParser.RegionDecompress(_saveSystem, saveConfig, regionCoord);

                RegionMap.Add(regionCoord, region);
            }

            if (!RegionMap[regionCoord].IsChunkLoaded(chunkLocalCoord))
            {
                RegionMap[regionCoord].AcquireChunkloadTesk(chunkLocalCoord);
            }
        }

        private async void PerformLoadChunkObjTask()
        {
            if (ChunkLoadTask.Count == 0) return;
            await UniTask.Yield();
            Vector2Int chunkLocalCoord = ChunkLoadTask.Dequeue();
            SpecificChunkData(chunkLocalCoord);

            for (int i = 0; i < 4; i++)
            {
                Vector2Int adjacentChunk = chunkLocalCoord + _adjacentChunks[i];
                SpecificChunkData(adjacentChunk);
            }
            if (!RegionMap[M13Mathf.ChunkToRegionCoord(chunkLocalCoord)].ChunkMap.ContainsKey(chunkLocalCoord))
                Debug.LogError($"����{M13Mathf.ChunkToRegionCoord(chunkLocalCoord)}������{chunkLocalCoord}���ݻ�δ������");
            ChunkObjLoad(chunkLocalCoord);
        }

        

        private async void PerformUnloadChunkObjTask()
        {
            if (ChunkUnloadTask.Count == 0) return;
            await UniTask.Yield();
            Vector2Int chunkLocalCoord = ChunkUnloadTask.Dequeue();

            if (!ChunkObjMap.ContainsKey(chunkLocalCoord)) return;
            ChunkObject.ChunkObjectRecycle(ChunkObjMap[chunkLocalCoord]);
            ChunkObjMap.Remove(chunkLocalCoord);
            foreach (var r in RegionMap)
            {
                if (r.Value.IsChunkOnThisRegion(chunkLocalCoord) && r.Value.ChunkMap.ContainsKey(chunkLocalCoord))
                {
                    r.Value.AcquireChunkUnloadTesk(chunkLocalCoord);
                    break;
                }
            }
        }
        #endregion

        private void ChunkObjLoad(Vector2Int chunkLocalCoord)
        {
            if (ChunkObjMap.Keys.Contains(chunkLocalCoord)) return;

            List<byte[]> neighborChunkHeightMaps = new List<byte[]>();

            _chm = (RegionMap[M13Mathf.ChunkToRegionCoord(chunkLocalCoord)].ChunkMap[chunkLocalCoord].ChunkHeightMap);
            _ctim = (RegionMap[M13Mathf.ChunkToRegionCoord(chunkLocalCoord)].ChunkMap[chunkLocalCoord].ChunkTurfIdMap);

            neighborChunkHeightMaps.Add((GetChunk(chunkLocalCoord + _adjacentChunks[0]).ChunkHeightMap));
            neighborChunkHeightMaps.Add((GetChunk(chunkLocalCoord + _adjacentChunks[1]).ChunkHeightMap));
            neighborChunkHeightMaps.Add((GetChunk(chunkLocalCoord + _adjacentChunks[2]).ChunkHeightMap));
            neighborChunkHeightMaps.Add((GetChunk(chunkLocalCoord + _adjacentChunks[3]).ChunkHeightMap));

            try
            {
                ChunkMesh cmesh = new ChunkMesh(_chm, neighborChunkHeightMaps, _ctim);
                ChunkObjMap.Add(chunkLocalCoord, ChunkObject.ChunkInstantiate(chunkLocalCoord, cmesh, transform));
                neighborChunkHeightMaps.Clear();
            }
            catch
            {
                Debug.Log($"����Ϊ�յ��������꣺{chunkLocalCoord}");
            }
            
        }

       
        private void UpdateChunkAroundPlayer(bool isInit)
        {
            if (isInit) lastChunkLocalCoordPlayerIn = chunkLocalCoordPlayerIn + new Vector2Int(3, 3);

            Vector2Int lastChunkCenter = lastChunkLocalCoordPlayerIn * 16 + new Vector2Int(8, 8);
            if (chunkLocalCoordPlayerIn != lastChunkLocalCoordPlayerIn && (MathF.Abs(playerV2Pos.x - lastChunkCenter.x) > 16 || MathF.Abs(playerV2Pos.y - lastChunkCenter.y) > 16))
            {
                lastChunkLocalCoordPlayerIn = chunkLocalCoordPlayerIn;
            }
            else return;
            chunkAroundPlayerIn.Clear();
            for (int x = chunkLocalCoordPlayerIn.x - _chunkLoadRange; x <= chunkLocalCoordPlayerIn.x + _chunkLoadRange; x++)
            {
                for (int y = chunkLocalCoordPlayerIn.x - _chunkLoadRange; y <= chunkLocalCoordPlayerIn.x + _chunkLoadRange; y++)
                {
                    if (M13Mathf.CalculateManhattanDistence(new Vector2Int(x, y), chunkLocalCoordPlayerIn) < _chunkLoadRange)
                        chunkAroundPlayerIn.Add(new Vector2Int(x, y));
                }
            }
            CreateLoadChunkObjTask();
            CreateUnloadChunkObjTask();
        }

        /// <summary>
        /// 创建区块场景加载任务
        /// </summary>
        private void CreateLoadChunkObjTask()
        {
            for(int i = 0; i < chunkAroundPlayerIn.Count; i++)
            {
                if (!ChunkObjMap.ContainsKey(chunkAroundPlayerIn[i]))
                {
                    ChunkLoadTask.Enqueue(chunkAroundPlayerIn[i]);
                }
            }
        }
        private void CreateUnloadChunkObjTask()
        {
            for (int i = 0; i < ChunkObjMap.Count; i++)
            {
                if (!chunkAroundPlayerIn.Contains(ChunkObjMap.Keys.ElementAt(i)))
                {
                    ChunkUnloadTask.Enqueue(ChunkObjMap.Keys.ElementAt(i));
                }
            }
        }

        private Chunk GetChunk(Vector2Int chunkLocalCoord)
        {
            return RegionMap[M13Mathf.ChunkToRegionCoord(chunkLocalCoord)].ChunkMap[chunkLocalCoord];
        }

        private bool IsChunkLoaded(Vector2Int chunkLocalCoord)
        {
            return RegionMap[M13Mathf.ChunkToRegionCoord(chunkLocalCoord)].ChunkMap.ContainsKey(chunkLocalCoord);
        }

        private void UpdateLoadStatus()
        {
            if (ChunkLoadTask.Count != 0)
            {
                worldDynamicLoadStatus = WorldDynamicLoadStatus.ChunkLoadState;
                return;
            }
            else if (ChunkUnloadTask.Count != 0 && ChunkLoadTask.Count == 0)
            {
                worldDynamicLoadStatus = WorldDynamicLoadStatus.ChunkUnloadState;
                return;
            }
            else if (ChunkUnloadTask.Count == 0)
            {
                worldDynamicLoadStatus = WorldDynamicLoadStatus.Empty;
                return;
            }
        }

#if UNITY_EDITOR
        private void DebugChunk(Vector2Int chunkLocalCoord, Color color)
        {
            DebugUtil.DrawChunkGrid(chunkLocalCoord, color);
        }
#endif
        public IArchitecture GetArchitecture()
        {
            return GameArchitecture.Interface;
        }

    }

    public enum WorldDynamicLoadStatus
    {
        Empty,
        ChunkLoadState,
        ChunkUnloadState
    }
}